//!
use std::os;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use core::registry::PackageRegistry;
use core::{MultiShell, Source, SourceId, PackageSet, Target, PackageId};
+use core::{Package, Summary, Resolve};
use core::resolver;
use ops;
use sources::{PathSource};
let source_id = package.get_package_id().get_source_id();
let mut config = try!(Config::new(*shell, update, jobs, target.clone()));
-
let mut registry = PackageRegistry::new(&mut config);
- let resolved = match try!(ops::load_lockfile(&lockfile, source_id)) {
- Some(r) => {
- try!(registry.add_sources(r.iter().map(|p| {
- p.get_source_id().clone()
- }).collect()));
- r
- }
- None => {
- try!(registry.add_sources(package.get_source_ids()));
- try!(resolver::resolve(package.get_package_id(),
- package.get_dependencies(),
- &mut registry))
- }
- };
+ match try!(ops::load_lockfile(&lockfile, source_id)) {
+ Some(r) => try!(add_lockfile_sources(&mut registry, &package, &r)),
+ None => try!(registry.add_sources(package.get_source_ids())),
+ }
- try!(registry.add_overrides(override_ids));
+ let resolved = try!(resolver::resolve(package.get_package_id(),
+ package.get_dependencies(),
+ &mut registry));
+ try!(registry.add_overrides(override_ids));
let resolved_with_overrides =
try!(resolver::resolve(package.get_package_id(),
package.get_dependencies(),
Ok(())
}
+
+/// When a lockfile is present, we want to keep as many dependencies at their
+/// original revision as possible. We need to account, however, for
+/// modifications to the manifest in terms of modifying, adding, or deleting
+/// dependencies.
+///
+/// This method will add any appropriate sources from the lockfile into the
+/// registry, and add all other sources from the root package to the registry.
+/// Any dependency which has not been modified has its source added to the
+/// registry (to retain the precise field if possible). Any dependency which
+/// *has* changed has its source id listed in the manifest added and all of its
+/// transitive dependencies are blacklisted to not be added from the lockfile.
+///
+/// TODO: this won't work too well for registry-based packages, but we don't
+/// have many of those anyway so we should be ok for now.
+fn add_lockfile_sources(registry: &mut PackageRegistry,
+ root: &Package,
+ resolve: &Resolve) -> CargoResult<()> {
+ let deps = resolve.deps(root.get_package_id()).move_iter().flat_map(|deps| {
+ deps.map(|d| (d.get_name(), d))
+ }).collect::<HashMap<_, _>>();
+
+ let mut sources = vec![root.get_package_id().get_source_id().clone()];
+ let mut to_avoid = HashSet::new();
+ let mut to_add = HashSet::new();
+ for dep in root.get_dependencies().iter() {
+ match deps.find(&dep.get_name()) {
+ Some(&lockfile_dep) => {
+ let summary = Summary::new(lockfile_dep, []);
+ if dep.matches(&summary) {
+ fill_with_deps(resolve, lockfile_dep, &mut to_add);
+ } else {
+ fill_with_deps(resolve, lockfile_dep, &mut to_avoid);
+ sources.push(dep.get_source_id().clone());
+ }
+ }
+ None => sources.push(dep.get_source_id().clone()),
+ }
+ }
+
+ // Only afterward once we know the entire blacklist are the lockfile
+ // sources added.
+ for addition in to_add.iter() {
+ if !to_avoid.contains(addition) {
+ sources.push(addition.get_source_id().clone());
+ }
+ }
+
+ return registry.add_sources(sources);
+
+ fn fill_with_deps<'a>(resolve: &'a Resolve, dep: &'a PackageId,
+ set: &mut HashSet<&'a PackageId>) {
+ if !set.insert(dep) { return }
+ for mut deps in resolve.deps(dep).move_iter() {
+ for dep in deps {
+ fill_with_deps(resolve, dep, set);
+ }
+ }
+ }
+}
execs().with_status(0));
assert_eq!(lockfile.stat().assert().modified, mtime);
})
+
+test!(adding_and_removing_packages {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.1"
+ "#)
+ .file("src/main.rs", "fn main() {}");
+
+ assert_that(p.cargo_process("cargo-generate-lockfile"),
+ execs().with_status(0));
+
+ let lockfile = p.root().join("Cargo.lock");
+ let toml = p.root().join("Cargo.toml");
+ let lock1 = File::open(&lockfile).read_to_string().assert();
+
+ // add a dep
+ File::create(&toml).write_str(r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.5.0"
+ "#).assert();
+ assert_that(p.process(cargo_dir().join("cargo-generate-lockfile")),
+ execs().with_status(0));
+ let lock2 = File::open(&lockfile).read_to_string().assert();
+ assert!(lock1 != lock2);
+
+ // change the dep
+ File::create(&toml).write_str(r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.2.0"
+ "#).assert();
+ assert_that(p.process(cargo_dir().join("cargo-generate-lockfile")),
+ execs().with_status(0));
+ let lock3 = File::open(&lockfile).read_to_string().assert();
+ assert!(lock1 != lock3);
+ assert!(lock2 != lock3);
+
+ // remove the dep
+ File::create(&toml).write_str(r#"
+ [package]
+ name = "foo"
+ authors = []
+ version = "0.0.1"
+ "#).assert();
+ assert_that(p.process(cargo_dir().join("cargo-generate-lockfile")),
+ execs().with_status(0));
+ let lock4 = File::open(&lockfile).read_to_string().assert();
+ assert_eq!(lock1, lock4);
+})